/**
 * 函数防抖
 * 通过延迟执行来限制函数的执行频率，常用于控制高频事件
 *
 * 防抖函数会等待一段时间（delay）才执行函数fn。如果在等待过程中又调用了防抖函数，
 * 它会重新等待，直到再次达到等待时间才会执行函数fn。这意味着函数fn只会执行一次，
 * 而且是在最后一次调用防抖函数后的delay时间后执行。这对于节制频繁事件（如窗口调整大小、
 * 滚动事件等）非常有用。
 *
 * @param {Function} fn - 待执行的函数
 * @param {number} delay - 延迟执行的时间间隔，默认为200毫秒
 * @param {boolean} immediate - 是否在首次调用时立即执行函数，默认为false
 * @returns {Function} - 返回一个新的函数，该函数将在delay时间间隔后执行原函数
 */
export function debounce(fn, delay = 200, immediate = false) {
  // 检查第一个参数是否为函数，如果不是抛出错误
  if (typeof fn !== 'function') {
    throw new TypeError('First argument must be a function');
  }
  // 检查第二个参数是否为非负数，如果不是抛出错误
  if (typeof delay !== 'number' || delay < 0) {
    throw new Error('Second argument must be a non-negative number');
  }

  // 用于存储定时器的id
  let timeoutId;

  // 返回一个新的函数，该函数将在delay时间间隔后执行原函数
  return function(...args) {
    // 保存函数执行的上下文
    const context = this;
    // 定义延迟执行的函数
    const later = () => {
      // 重置定时器
      timeoutId = null;
      // 如果不是立即执行，则执行函数
      if (!immediate) {
        fn.apply(context, args);
      }
    };

    // 判断是否需要立即执行函数
    const callNow = immediate && !timeoutId;
    // 清除现有的定时器
    clearTimeout(timeoutId);
    // 设置新的定时器
    timeoutId = setTimeout(later, delay);

    // 如果需要立即执行，则执行函数
    if (callNow) {
      fn.apply(context, args);
    }
  };
}


/**
 * 节流函数，用于限制函数调用频率
 * 在事件处理函数中很有用，例如滚动和调整窗口大小，以防止函数过于频繁地执行
 * 函数节流，频繁操作中间隔 delay 的时间才处理一次
 * @param {Function} fn 要节流的函数
 * @param {number} delay 节流延迟时间，默认为200毫秒
 * @returns {Function} 返回节流后的函数
 */
export function throttle(fn, delay = 200) {  // 使用默认参数
  let timer = null;
  let lastCallTime = 0;

  return function(...args) {
    const now = Date.now();

    // 首次调用时执行一次, 设置开始时间
    if (lastCallTime === 0) {
      lastCallTime = now;
      fn.apply(this, args);  // 立即执行一次
      return;
    }

    // 如果存在一个未完成的延迟，则清除它
    if (timer !== null) {
      try {
        clearTimeout(timer);
      } catch (error) {
        console.error('Error clearing timeout:', error);
        // 添加更严格的错误处理，例如重新抛出错误或记录更详细的日志
        throw error;  // 重新抛出错误
      }
    }

    // 重置timer
    timer = null;

    if (now - lastCallTime >= delay) {
      fn.apply(this, args);
      lastCallTime = now;
    } else {
      timer = setTimeout(() => {
        try {
          fn.apply(this, args);
          lastCallTime = Date.now();  // 在执行后更新 lastCallTime
        } catch (error) {
          console.error('Error executing function:', error);
        }
      }, delay);
    }
  };
}
